import { openDB } from "idb";
import { getLengthOfAny, getTypeOfAny } from "../utilities.mjs";
import { DataString } from "../data.mts";

async function getAllStoreNames() {
  const result = new Map<IDBDatabaseInfo, DOMStringList>();
  const promises: Promise<void>[] = [];
  const dbs = await indexedDB.databases();
  for (const key of dbs) {
    promises.push(
      new Promise((resolve, reject) => {
        if (key.name) {
          const openRequest = indexedDB.open(key.name, key.version);
          openRequest.addEventListener("error", (error) => {
            throw error;
          });
          openRequest.addEventListener("success", () => {
            if (openRequest.result) {
              result.set(key, openRequest.result.objectStoreNames);
              resolve();
            } else {
              throw openRequest.error ?? new Error(JSON.stringify(key));
            }
          });
        } else {
          reject(new Error(JSON.stringify(key)));
        }
      }),
    );
  }
  await Promise.allSettled(promises);
  return result;
}

const query = async () => {
  const stns = await getAllStoreNames();
  const data: {
    name: string;
    version?: number;
    tables: {
      name: string;
      kvs: Record<string | number, unknown>;
    }[];
  }[] = [];
  let result = 0;
  for (const [key, value] of stns) {
    if (typeof key.name == "string") {
      result += (key.name.length ?? 0) + (key.version?.toString().length ?? 0);
      const database = await openDB(key.name, key.version);
      const cdata: (typeof data)[number] = {
        ...(key as {
          name: string;
          version?: number;
        }),
        tables: [],
      };
      for (const element of value) {
        const cos: Record<string, unknown> = {};
        const osks = await database.getAllKeys(element);
        result += element.length;
        for (const osk of osks) {
          cos[osk as string] = await database.get(element, osk);
          result += getLengthOfAny(osk) + getLengthOfAny(cos[osk as string]);
        }
        cdata.tables.push({
          name: element,
          kvs: cos,
        });
      }
      data.push(cdata);
    }
  }
  return { count: stns.size, result, data };
};

const remove = (key: string) =>
  new Promise<void>((resolve) => {
    const deleteRequest = indexedDB.deleteDatabase(key);
    deleteRequest.addEventListener("error", (error) => {
      throw error;
    });
    deleteRequest.addEventListener("success", () => {
      resolve();
    });
  });

const clear = async () => {
  const dbs = await indexedDB.databases();
  const promises: Promise<void>[] = [];
  for (const database of dbs) {
    const key = database.name;
    if (typeof key == "string") {
      promises.push(remove(key));
    }
  }
  return await Promise.allSettled(promises);
};

const summary = (
  data: Awaited<ReturnType<typeof query>>,
) => `${DataString.indexedDB}：
数据库数量 ${String(data.count)}
占用大小 ${String(data.result)}`;

const stringify = (data: Awaited<ReturnType<typeof query>>) => {
  let prompts = "";
  const dbss = data.data;
  for (const [index, database] of dbss.entries()) {
    prompts += `------
数据库 ${[index + 1, dbss.length].join("/")} ：${database.name} 版本 ${String(database.version ?? 1)}
表数量：${String(database.tables.length)}
`;
    for (const [index_, table] of database.tables.entries()) {
      const keys = Object.keys(table.kvs);
      prompts += `------
表 ${[index_ + 1, database.tables.length].join("/")} ：${table.name}
键数量：${String(keys.length)}
`;
      for (const [k, key] of keys.entries()) {
        prompts += `------
键 ${[k + 1, keys.length].join("/")} ：${key}
值类型：${getTypeOfAny(table.kvs[key])}
值大小：${String(getLengthOfAny(table.kvs[key]))}
`;
      }
    }
  }
  return prompts;
};

export default { query, clear, remove, summary, stringify };
